﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SL.M.Collections
{
    /// <summary>
    /// Represent an individual dispatch source.
    /// </summary>
    public interface IDispatchSource
    {
        /// <summary>
        /// Return if this source matches the specified message.
        /// </summary>
        /// <param name="process">The process to check.</param>
        /// <returns>True if the sources match.</returns>
        bool Matches(Process process);
    }

    /// <summary>
    /// Represent the source of a dispatch table.
    /// </summary>
    public static class DispatchSource
    {
        class ProcessDispatchSource : IDispatchSource
        {
            Process _process;

            public ProcessDispatchSource(Process process)
            {
                _process = process;
            }

            public override bool Equals(object obj)
            {
                if (obj is ProcessDispatchSource)
                {
                    return Equals((ProcessDispatchSource)obj);
                }
                return base.Equals(obj);
            }

            public override int GetHashCode()
            {
                return _process.GetHashCode();
            }

            public bool Equals(ProcessDispatchSource other)
            {
                if (other != null)
                {
                    return _process == other._process;
                }
                return false;
            }

            public bool Matches(Process process)
            {
                if (process != null)
                {
                    return _process == process;
                }
                return false;
            }
        }

        class AnyProcessDispatchSource : IDispatchSource, IEquatable<AnyProcessDispatchSource>
        {
            public override bool Equals(object obj)
            {
                if (obj is AnyProcessDispatchSource)
                {
                    return true;
                }
                return base.Equals(obj);
            }

            public override int GetHashCode()
            {
                return 0;
            }

            public bool Equals(AnyProcessDispatchSource other)
            {
                if (other != null)
                {
                    return true;
                }
                return false;
            }

            public bool Matches(Process process)
            {
                return true;
            }
        }

        class ProcessTypeDispatchSource : IDispatchSource, IEquatable<ProcessTypeDispatchSource>
        {
            Type _type;

            public ProcessTypeDispatchSource(Type type)
            {
                _type = type;
            }

            public override bool Equals(object obj)
            {
                if (obj is ProcessTypeDispatchSource)
                {
                    return Equals((ProcessTypeDispatchSource)obj);
                }
                return base.Equals(obj);
            }

            public override int GetHashCode()
            {
                return _type.GetHashCode();
            }

            public bool Equals(ProcessTypeDispatchSource other)
            {
                if (other != null)
                {
                    return _type.Equals(other._type);
                }
                return false;
            }

            public bool Matches(Process process)
            {
                if (process != null)
                {
                    return _type.IsAssignableFrom(process.GetType());
                }
                return false;
            }
        }

        class ProcessTypeAggregateSource : IDispatchSource, IEquatable<ProcessTypeAggregateSource>
        {
            IDispatchSource[] _sources;

            public ProcessTypeAggregateSource(IDispatchSource[] sources)
            {
                _sources = sources;
            }

            public override bool Equals(object obj)
            {
                if (obj is ProcessTypeAggregateSource)
                {
                    return Equals((ProcessTypeAggregateSource)obj);
                }
                return base.Equals(obj);
            }

            public override int GetHashCode()
            {
                int ret = 0;
                foreach (IDispatchSource source in _sources)
                {
                    ret += source.GetHashCode();
                }
                return ret;
            }

            public bool Equals(ProcessTypeAggregateSource other)
            {
                if (other != null)
                {
                    if (_sources.Length == other._sources.Length)
                    {
                        for (int i = 0; i < _sources.Length; ++i)
                        {
                            if (!_sources[i].Equals(other._sources[i]))
                            {
                                return false;
                            }
                        }
                        return true;
                    }
                }
                return false;
            }

            public bool Matches(Process process)
            {
                if (process != null)
                {
                    foreach (IDispatchSource source in _sources)
                    {
                        if (source.Matches(process))
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }

        /// <summary>
        /// Return a dispatch source for the given process.
        /// </summary>
        /// <param name="sender">The sender process.</param>
        /// <returns>A dispatch source.</returns>
        public static IDispatchSource FromProcess(Process sender)
        {
            if (sender == null)
            {
                throw new ArgumentNullException("sender");
            }

            return new ProcessDispatchSource(sender);
        }

        /// <summary>
        /// Returns a dispatch source for any process.
        /// </summary>
        /// <returns>A dispatch source.</returns>
        public static IDispatchSource FromAnyProcess()
        {
            return new AnyProcessDispatchSource();
        } 

        /// <summary>
        /// Returns a dispatch source from a process of the given type.
        /// </summary>
        /// <param name="processType">The type of process to accept messages from.</param>
        /// <returns>A dispatch source.</returns>
        public static IDispatchSource FromProcessOfType<T>() where T : Process
        {
            return new ProcessTypeDispatchSource(typeof(T));
        }

        /// <summary>
        /// Returns an aggregate dispatch source which includes multiple types of sources.
        /// </summary>
        /// <param name="sources">The list of sources to include.</param>
        /// <returns>A dispatch source.</returns>
        public static IDispatchSource AggregateFromSources(params IDispatchSource[] sources)
        {
            if (sources == null || sources.Length == 0)
            {
                throw new ArgumentNullException("sources");
            }

            return new ProcessTypeAggregateSource(sources);
        }
    }
}
